// This function helps calculate and snap time based on mouse Y position function getTimeFromYPos(mouseYInSlot, slotClientHeight) { // PIXELS_PER_HOUR should be available from your main script's scope const rawHours = mouseYInSlot / PIXELS_PER_HOUR; // Clamp rawHours to prevent calculation errors if mouse is slightly outside bounds const clampedHours = Math.max(0, Math.min(24, rawHours)); let hourPart = Math.floor(clampedHours); let minutePart = Math.round(((clampedHours - hourPart) * 60) / 15) * 15; // Snap to 15 min if (minutePart >= 60) { // Handle minute overflow hourPart += 1; minutePart = 0; } if (hourPart >= 24) { // Cap at the end of the day for display within a single day column hourPart = 23; minutePart = 59; // Could also be 45 if strictly last 15-min slot of 23:45 } return { hour: hourPart, minute: minutePart }; } function setupTimeHoverBar() { const agendaContainer = document.getElementById('agenda-container'); if (!agendaContainer) return; let hoverBar = document.querySelector('.time-hover-bar'); if (!hoverBar) { hoverBar = document.createElement('div'); hoverBar.className = 'time-hover-bar'; const timeLabel = document.createElement('span'); timeLabel.className = 'time-label'; hoverBar.appendChild(timeLabel); // The bar will be appended to the correct '.time-slots' on mousemove } const hoverBarTimeLabel = hoverBar.querySelector('.time-label'); let currentHoveredTimeSlots = null; agendaContainer.addEventListener('mousemove', function(event) { const timeSlots = event.target.closest('.time-slots'); // Show bar if over time-slots, but not over a battle-block or the time-ruler itself if (timeSlots && !event.target.closest('.battle-block') && !event.target.closest('.time-ruler')) { if (currentHoveredTimeSlots !== timeSlots || hoverBar.parentNode !== timeSlots) { timeSlots.appendChild(hoverBar); // Append/move bar to current time-slots currentHoveredTimeSlots = timeSlots; } const rect = timeSlots.getBoundingClientRect(); const mouseY = event.clientY - rect.top; // Ensure mouse is vertically within the time-slots if (mouseY < 0 || mouseY > timeSlots.clientHeight) { hoverBar.style.display = 'none'; return; } const time = getTimeFromYPos(mouseY, timeSlots.clientHeight); const displayHour = time.hour; const displayMinute = time.minute; const snappedY = (displayHour + displayMinute / 60) * PIXELS_PER_HOUR; hoverBar.style.top = `${snappedY}px`; hoverBarTimeLabel.textContent = `${String(displayHour).padStart(2, '0')}:${String(displayMinute).padStart(2, '0')}`; hoverBar.style.display = 'block'; } else { hoverBar.style.display = 'none'; // Hide if not over a valid free space currentHoveredTimeSlots = null; } }); agendaContainer.addEventListener('mouseleave', function(event) { // Hide bar if mouse leaves the entire agenda container if (!agendaContainer.contains(event.relatedTarget)) { hoverBar.style.display = 'none'; currentHoveredTimeSlots = null; } }); } function setupNewEventModal() { const newModal = document.getElementById('new-event-modal'); const newModalCloseButton = document.getElementById('new-event-modal-close'); const agendaContainer = document.getElementById('agenda-container'); const eventDateInput = document.getElementById('event-date'); const eventTimeInput = document.getElementById('event-time'); const countdownDaysSpan = document.getElementById('countdown-days'); const countdownHoursSpan = document.getElementById('countdown-hours'); const countdownMinutesSpan = document.getElementById('countdown-minutes'); const countdownTextElem = document.getElementById('countdown-display').querySelector('p'); if (!newModal || !newModalCloseButton || !agendaContainer || !eventDateInput || !eventTimeInput || !countdownTextElem) { console.error("New event modal elements not found. Aborting setup."); return; } // Modal close mechanisms newModalCloseButton.onclick = () => { newModal.style.display = "none"; }; window.addEventListener('click', (event) => { if (event.target == newModal) newModal.style.display = "none"; }); window.addEventListener('keydown', (event) => { if (event.key === 'Escape' && newModal.style.display === "block") newModal.style.display = "none"; }); // Open modal on click in a free space within time-slots agendaContainer.addEventListener('click', function(event) { const timeSlots = event.target.closest('.time-slots'); // Ensure click is on time-slots, not on a battle block or the time ruler if (timeSlots && !event.target.closest('.battle-block') && !event.target.closest('.time-ruler')) { const dayColumn = timeSlots.closest('.day-column'); if (!dayColumn || !dayColumn.dataset.date) return; const columnDate = new Date(dayColumn.dataset.date); const rect = timeSlots.getBoundingClientRect(); const mouseY = event.clientY - rect.top; const time = getTimeFromYPos(mouseY, timeSlots.clientHeight); const hourPart = time.hour; const minutePart = time.minute; // Pre-fill date and time inputs eventDateInput.value = `${columnDate.getFullYear()}-${String(columnDate.getMonth() + 1).padStart(2, '0')}-${String(columnDate.getDate()).padStart(2, '0')}`; eventTimeInput.value = `${String(hourPart).padStart(2, '0')}:${String(minutePart).padStart(2, '0')}`; updateCountdown(); // Initial countdown calculation newModal.style.display = 'block'; } }); // Countdown logic function updateCountdown() { if (!eventDateInput.value || !eventTimeInput.value) { countdownDaysSpan.textContent = '--'; countdownHoursSpan.textContent = '--'; countdownMinutesSpan.textContent = '--'; countdownTextElem.firstChild.textContent = "Remaining: "; // Default text return; } const targetDateTimeStr = `${eventDateInput.value}T${eventTimeInput.value}:00`; try { const targetDate = new Date(targetDateTimeStr); if (isNaN(targetDate.getTime())) { // Invalid date/time input countdownDaysSpan.textContent = '??'; countdownHoursSpan.textContent = '??'; countdownMinutesSpan.textContent = '??'; return; } const now = new Date(); let diff = targetDate.getTime() - now.getTime(); if (diff < 0) { countdownTextElem.firstChild.textContent = "Time since: "; diff = Math.abs(diff); // Use absolute difference for past events } else { countdownTextElem.firstChild.textContent = "Remaining: "; } const days = Math.floor(diff / (1000 * 60 * 60 * 24)); diff -= days * (1000 * 60 * 60 * 24); const hours = Math.floor(diff / (1000 * 60 * 60)); diff -= hours * (1000 * 60 * 60); const minutes = Math.floor(diff / (1000 * 60)); countdownDaysSpan.textContent = days; countdownHoursSpan.textContent = hours; countdownMinutesSpan.textContent = minutes; } catch (e) { console.error("Error parsing date/time for countdown:", e); countdownDaysSpan.textContent = 'Err'; countdownHoursSpan.textContent = 'Err'; countdownMinutesSpan.textContent = 'Err'; } } eventDateInput.addEventListener('change', updateCountdown); eventTimeInput.addEventListener('change', updateCountdown); // Update countdown periodically if modal is open setInterval(() => { if (newModal.style.display === 'block') { updateCountdown(); } }, 30000); // Update every 30 seconds } function initializeCustomFeatures() { setupTimeHoverBar(); setupNewEventModal(); }